A handout by Casey Dunn

This page can be viewed at https://rawgit.com/Brown-BIOL2430-S04-Fall2015/syllabus/master/data_visualization_handout.nb.html

What is data visualization?

Data visualization is the mapping of data attributes to aesthetic attributes.

Data attributes

Data attributes are direct properties of the data or summaries of the data. Statistical principals provide our language for summarizing data. Data attributes can include:

  • Magnitude
  • Category
  • Mean
  • Variance
  • Median
  • Density
  • Regression lines

Data visualizations often include both raw data properties (e.g., a scatter plot where magnitude is mapped to position) and data summaries (e.g., a regression line).

Aesthetic attributes

Aesthetic attributes are visual properties that can be varied. The Elements and Principals of Design provide our language for describing aesthetic attributes. Aesthetic attributes include:

  • X position
  • Y position
  • Color
  • Size
  • Shape
  • Texture
  • Motion

Here are a couple figures from Paper Leaf Design that summarize these attributes:

The act of creating a data visualization

When you create a data visualization, you are deciding how to map data to aesthetics. You can use one of the very common mappings, such as a scatter plot or bar chart. If you do this, you can use a canned visualization with default line widths, colors, typefaces, etc, or you can customize it to a greater or lesser degree. These common mappings have the benefit of being familiar to your audience and fitting many goals well. They are often constraining, though, and you should be careful to not misapply them just because they are readily available.

Rather than use a common mapping, you can make your own. This has gotten much easier in the last few years with tools like d3 and ggplot.

There are several big decisions.

Should I apply data reduction?

There are a few reasons you may want to reduce data before visualization:

  • Not all the data are relevant to the question at hand
  • Not all the data are needed to convey the overall point (eg, a reduced number of data elements tells the same story as all the data, but more clearly)
  • It isn’t technically feasible to show all the data

One way to reduce the data is to exclude particular observations: - Exclude some observations randomly, eg to rarify the data - Exclude some observations based on their values, eg exclude data points with large values to focus on what is happening with data points with smaller values. In this sense zooming and cropping is a form of data reduction.

Alternatively, one can exlude or reduce the number of variables: - You may just be interested in a subset of variables for each observation, and only show those. - You may ramap higher dimensional data to a lower number of dimensions. For example, PCA rotates the data to show the axes with the greatest variance. Often PCA axes with low variance are then discarded.

Which aesthetic properties should I map the data to?

A big part of answering this question depends on what the final medium of the visualization is. For example, print is generally more constraining than web. Position, size, and color are typically the easiest attributes to quickly interpret. In general use these before considering other attibutes.

Should there be auesthetic attributes that are not mapped to data?

Visualizations can also contain easthetic elements that are not mapped to data. These include axes, labels, background images, tic marks, drop shaddows, legends, and a variety of decorations.

The value of these added elements is often overestimated and the cost underestimated. The main cost is distraction. Approach this decision by deciding what is absolutely necessary, and then carefully considering the value of each added item. If there is little or no added value don’t add the element.

Guiding principals for data visualization

Here is some general advice that applies in post situations:

  1. Reduce the number of visual elements that aren’t mapped to the data. This corresponds to Tufte’s concept of “minimal ink”. Axes and labels should be simple. If a visual element doesn’t change when the data changes, ask yourself if you really need it.
  2. Don’t map the same data attribute to multiple aesthetic attributes (eg, size and color of a point). This increases the cognitive load of interpretting the visualization without providing added value.
  3. If you are reducing the data, you are essentially looking at a shaddow of the data. Different objects can cast the same shaddow, so there is a stong possibility that you could miss important features of the data. Reduce the data in several different ways to cast a shaddow from different angles to test your assumptions about what you can’t see in each particular visualization.

People can get very dogmatic and extreme about data visualization goals, particularly in the paplication of points 1 and 2 above. As with any artform, interesting and productive things can happen when you break the “rules”. Just because it is often a bad idea to break the rules doesn’t mean it is always bad to break the rules. Here are a couple reasons to break the rules:

Examples

Simulate a dataset of three varaibles across 100 observations.

set.seed(12345)
  # Sigma = matrix(c(matrix(c(10,6,1,6,5,2,1,2,1),3,3)),3,3)
  Sigma = matrix(c(matrix(c(2,-1,0,-1,2,-1,0,-1,2),3,3)),3,3)
  D=data.frame(mvrnorm(n=100, mu=rep(0,3), Sigma))
  names(D)=c("x", "y", "z")

Subset the data by plotting only 2 of the 3 variables.

  library(ggplot2)
  ggplot(D,
    aes(y=y, x=x)) +
    geom_point()

Map all three aesthetic attributes.

  library(ggplot2)
  ggplot(D,
    aes(y=y, x=x, col=z)) +
    geom_point()

  library(ggplot2)
  ggplot(D,
    aes(y=y, x=x, size=z)) +
    geom_point()

Map y to multiple aesthetic attributes.

  library(ggplot2)
  ggplot(D,
    aes(y=y, x=x, col=y)) +
    geom_point()

  library(ggplot2)
  ggplot(D,
    aes(y=y, x=x, size=y)) +
    geom_point()

  library(ggplot2)
  ggplot(D,
    aes(y=y, x=x, size=y, col=y)) +
    geom_point()

Add aesthetic attributes that aren’t mapped to data.

library(png)
library(grid)
img <- readPNG("figures/cat.png")
g <- rasterGrob(img, interpolate=TRUE) 
  ggplot(D,
    aes(y=y, x=x)) +
    geom_point() +
    #facet_grid(.~Place) + 
    theme_bw() +
    annotation_custom(g, xmin=-Inf, xmax=Inf, ymin=-Inf, ymax=Inf)

LS0tCnRpdGxlOiAiRGF0YSB2aXN1YWxpemF0aW9uIgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKLS0tCgpBIGhhbmRvdXQgYnkgW0Nhc2V5IER1bm5dKGh0dHA6Ly9kdW5ubGFiLm9yZykKClRoaXMgcGFnZSBjYW4gYmUgdmlld2VkIGF0IGh0dHBzOi8vcmF3Z2l0LmNvbS9Ccm93bi1CSU9MMjQzMC1TMDQtRmFsbDIwMTUvc3lsbGFidXMvbWFzdGVyL2RhdGFfdmlzdWFsaXphdGlvbl9oYW5kb3V0Lm5iLmh0bWwKCgojIyBXaGF0IGlzIGRhdGEgdmlzdWFsaXphdGlvbj8KCkRhdGEgdmlzdWFsaXphdGlvbiBpcyB0aGUgbWFwcGluZyBvZiAqZGF0YSBhdHRyaWJ1dGVzKiB0byAqYWVzdGhldGljIGF0dHJpYnV0ZXMqLgoKIyMjIERhdGEgYXR0cmlidXRlcwoKRGF0YSBhdHRyaWJ1dGVzIGFyZSBkaXJlY3QgcHJvcGVydGllcyBvZiB0aGUgZGF0YSBvciBzdW1tYXJpZXMgb2YgdGhlIGRhdGEuICpTdGF0aXN0aWNhbCBwcmluY2lwYWxzKiBwcm92aWRlIG91ciBsYW5ndWFnZSBmb3Igc3VtbWFyaXppbmcgZGF0YS4gRGF0YSBhdHRyaWJ1dGVzIGNhbiBpbmNsdWRlOgoKLSBNYWduaXR1ZGUKLSBDYXRlZ29yeQotIE1lYW4KLSBWYXJpYW5jZQotIE1lZGlhbgotIERlbnNpdHkKLSBSZWdyZXNzaW9uIGxpbmVzCgpEYXRhIHZpc3VhbGl6YXRpb25zIG9mdGVuIGluY2x1ZGUgYm90aCByYXcgZGF0YSBwcm9wZXJ0aWVzIChlLmcuLCBhIHNjYXR0ZXIgcGxvdCB3aGVyZSBtYWduaXR1ZGUgaXMgbWFwcGVkIHRvIHBvc2l0aW9uKSBhbmQgZGF0YSBzdW1tYXJpZXMgKGUuZy4sIGEgcmVncmVzc2lvbiBsaW5lKS4gCgojIyMgQWVzdGhldGljIGF0dHJpYnV0ZXMKCkFlc3RoZXRpYyBhdHRyaWJ1dGVzIGFyZSB2aXN1YWwgcHJvcGVydGllcyB0aGF0IGNhbiBiZSB2YXJpZWQuIFRoZSAqRWxlbWVudHMgYW5kIFByaW5jaXBhbHMgb2YgRGVzaWduKiBwcm92aWRlIG91ciBsYW5ndWFnZSBmb3IgZGVzY3JpYmluZyBhZXN0aGV0aWMgYXR0cmlidXRlcy4gQWVzdGhldGljIGF0dHJpYnV0ZXMgaW5jbHVkZToKCi0gWCBwb3NpdGlvbgotIFkgcG9zaXRpb24KLSBDb2xvcgotIFNpemUKLSBTaGFwZQotIFRleHR1cmUKLSBNb3Rpb24KCgpIZXJlIGFyZSBhIGNvdXBsZSBmaWd1cmVzIGZyb20gW1BhcGVyIExlYWYgRGVzaWduXShodHRwczovL3BhcGVyLWxlYWYuY29tLykgdGhhdCBzdW1tYXJpemUgdGhlc2UgYXR0cmlidXRlczoKCiFbXShmaWd1cmVzL3BhcGVybGVhZl9kZXNpZ25fZWxlbWVudHMuanBnKQoKIVtdKGZpZ3VyZXMvcGFwZXJsZWFmX2Rlc2lnbl9wcmluY2lwYWxzLmpwZykKCiMjIFRoZSBhY3Qgb2YgY3JlYXRpbmcgYSBkYXRhIHZpc3VhbGl6YXRpb24KCldoZW4geW91IGNyZWF0ZSBhIGRhdGEgdmlzdWFsaXphdGlvbiwgeW91IGFyZSBkZWNpZGluZyBob3cgdG8gbWFwIGRhdGEgdG8gYWVzdGhldGljcy4gWW91IGNhbiB1c2Ugb25lIG9mIHRoZSB2ZXJ5IGNvbW1vbiBtYXBwaW5ncywgc3VjaCBhcyBhIHNjYXR0ZXIgcGxvdCBvciBiYXIgY2hhcnQuIElmIHlvdSBkbyB0aGlzLCB5b3UgY2FuIHVzZSBhIGNhbm5lZCB2aXN1YWxpemF0aW9uIHdpdGggZGVmYXVsdCBsaW5lIHdpZHRocywgY29sb3JzLCB0eXBlZmFjZXMsIGV0Yywgb3IgeW91IGNhbiBjdXN0b21pemUgaXQgdG8gYSBncmVhdGVyIG9yIGxlc3NlciBkZWdyZWUuIFRoZXNlIGNvbW1vbiBtYXBwaW5ncyBoYXZlIHRoZSBiZW5lZml0IG9mIGJlaW5nIGZhbWlsaWFyIHRvIHlvdXIgYXVkaWVuY2UgYW5kIGZpdHRpbmcgbWFueSBnb2FscyB3ZWxsLiBUaGV5IGFyZSBvZnRlbiBjb25zdHJhaW5pbmcsIHRob3VnaCwgYW5kIHlvdSBzaG91bGQgYmUgY2FyZWZ1bCB0byBub3QgbWlzYXBwbHkgdGhlbSBqdXN0IGJlY2F1c2UgdGhleSBhcmUgcmVhZGlseSBhdmFpbGFibGUuCgpSYXRoZXIgdGhhbiB1c2UgYSBjb21tb24gbWFwcGluZywgeW91IGNhbiBtYWtlIHlvdXIgb3duLiBUaGlzIGhhcyBnb3R0ZW4gbXVjaCBlYXNpZXIgaW4gdGhlIGxhc3QgZmV3IHllYXJzIHdpdGggdG9vbHMgbGlrZSBbZDNdKGh0dHBzOi8vZDNqcy5vcmcvKSBhbmQgW2dncGxvdF0oaHR0cDovL3R1dG9yaWFscy5pcS5oYXJ2YXJkLmVkdS9SL1JncmFwaGljcy9SZ3JhcGhpY3MuaHRtbCkuCgpUaGVyZSBhcmUgc2V2ZXJhbCBiaWcgZGVjaXNpb25zLgoKIyMjIFNob3VsZCBJIGFwcGx5IGRhdGEgcmVkdWN0aW9uPwoKVGhlcmUgYXJlIGEgZmV3IHJlYXNvbnMgeW91IG1heSB3YW50IHRvIHJlZHVjZSBkYXRhIGJlZm9yZSB2aXN1YWxpemF0aW9uOgoKLSBOb3QgYWxsIHRoZSBkYXRhIGFyZSByZWxldmFudCB0byB0aGUgcXVlc3Rpb24gYXQgaGFuZAotIE5vdCBhbGwgdGhlIGRhdGEgYXJlIG5lZWRlZCB0byBjb252ZXkgdGhlIG92ZXJhbGwgcG9pbnQgKGVnLCBhIHJlZHVjZWQgbnVtYmVyIG9mIGRhdGEgZWxlbWVudHMgdGVsbHMgdGhlIHNhbWUgc3RvcnkgYXMgYWxsIHRoZSBkYXRhLCBidXQgbW9yZSBjbGVhcmx5KQotIEl0IGlzbid0IHRlY2huaWNhbGx5IGZlYXNpYmxlIHRvIHNob3cgYWxsIHRoZSBkYXRhCgpPbmUgd2F5IHRvIHJlZHVjZSB0aGUgZGF0YSBpcyB0byBleGNsdWRlIHBhcnRpY3VsYXIgb2JzZXJ2YXRpb25zOgotIEV4Y2x1ZGUgc29tZSBvYnNlcnZhdGlvbnMgcmFuZG9tbHksIGVnIHRvIHJhcmlmeSB0aGUgZGF0YQotIEV4Y2x1ZGUgc29tZSBvYnNlcnZhdGlvbnMgYmFzZWQgb24gdGhlaXIgdmFsdWVzLCBlZyBleGNsdWRlIGRhdGEgcG9pbnRzIHdpdGggbGFyZ2UgdmFsdWVzIHRvIGZvY3VzIG9uIHdoYXQgaXMgaGFwcGVuaW5nIHdpdGggZGF0YSBwb2ludHMgd2l0aCBzbWFsbGVyIHZhbHVlcy4gSW4gdGhpcyBzZW5zZSB6b29taW5nIGFuZCBjcm9wcGluZyBpcyBhIGZvcm0gb2YgZGF0YSByZWR1Y3Rpb24uCgpBbHRlcm5hdGl2ZWx5LCBvbmUgY2FuIGV4bHVkZSBvciByZWR1Y2UgdGhlIG51bWJlciBvZiB2YXJpYWJsZXM6Ci0gWW91IG1heSBqdXN0IGJlIGludGVyZXN0ZWQgaW4gYSBzdWJzZXQgb2YgdmFyaWFibGVzIGZvciBlYWNoIG9ic2VydmF0aW9uLCBhbmQgb25seSBzaG93IHRob3NlLgotIFlvdSBtYXkgcmFtYXAgaGlnaGVyIGRpbWVuc2lvbmFsIGRhdGEgdG8gYSBsb3dlciBudW1iZXIgb2YgZGltZW5zaW9ucy4gRm9yIGV4YW1wbGUsIFBDQSByb3RhdGVzIHRoZSBkYXRhIHRvIHNob3cgdGhlIGF4ZXMgd2l0aCB0aGUgZ3JlYXRlc3QgdmFyaWFuY2UuIE9mdGVuIFBDQSBheGVzIHdpdGggbG93IHZhcmlhbmNlIGFyZSB0aGVuIGRpc2NhcmRlZC4KCgojIyMgV2hpY2ggYWVzdGhldGljIHByb3BlcnRpZXMgc2hvdWxkIEkgbWFwIHRoZSBkYXRhIHRvPwoKQSBiaWcgcGFydCBvZiBhbnN3ZXJpbmcgdGhpcyBxdWVzdGlvbiBkZXBlbmRzIG9uIHdoYXQgdGhlIGZpbmFsIG1lZGl1bSBvZiB0aGUgdmlzdWFsaXphdGlvbiBpcy4gRm9yIGV4YW1wbGUsIHByaW50IGlzIGdlbmVyYWxseSBtb3JlIGNvbnN0cmFpbmluZyB0aGFuIHdlYi4gUG9zaXRpb24sIHNpemUsIGFuZCBjb2xvciBhcmUgdHlwaWNhbGx5IHRoZSBlYXNpZXN0IGF0dHJpYnV0ZXMgdG8gcXVpY2tseSBpbnRlcnByZXQuIEluIGdlbmVyYWwgdXNlIHRoZXNlIGJlZm9yZSBjb25zaWRlcmluZyBvdGhlciBhdHRpYnV0ZXMuCgojIyMgU2hvdWxkIHRoZXJlIGJlIGF1ZXN0aGV0aWMgYXR0cmlidXRlcyB0aGF0IGFyZSBub3QgbWFwcGVkIHRvIGRhdGE/CgpWaXN1YWxpemF0aW9ucyBjYW4gYWxzbyBjb250YWluIGVhc3RoZXRpYyBlbGVtZW50cyB0aGF0IGFyZSBub3QgbWFwcGVkIHRvIGRhdGEuIFRoZXNlIGluY2x1ZGUgYXhlcywgbGFiZWxzLCBiYWNrZ3JvdW5kIGltYWdlcywgdGljIG1hcmtzLCBkcm9wIHNoYWRkb3dzLCBsZWdlbmRzLCBhbmQgYSB2YXJpZXR5IG9mIGRlY29yYXRpb25zLgoKVGhlIHZhbHVlIG9mIHRoZXNlIGFkZGVkIGVsZW1lbnRzIGlzIG9mdGVuIG92ZXJlc3RpbWF0ZWQgYW5kIHRoZSBjb3N0IHVuZGVyZXN0aW1hdGVkLiBUaGUgbWFpbiBjb3N0IGlzIGRpc3RyYWN0aW9uLiBBcHByb2FjaCB0aGlzIGRlY2lzaW9uIGJ5IGRlY2lkaW5nIHdoYXQgaXMgYWJzb2x1dGVseSBuZWNlc3NhcnksIGFuZCB0aGVuIGNhcmVmdWxseSBjb25zaWRlcmluZyB0aGUgdmFsdWUgb2YgZWFjaCBhZGRlZCBpdGVtLiBJZiB0aGVyZSBpcyBsaXR0bGUgb3Igbm8gYWRkZWQgdmFsdWUgZG9uJ3QgYWRkIHRoZSBlbGVtZW50LiAKCiMjIEd1aWRpbmcgcHJpbmNpcGFscyBmb3IgZGF0YSB2aXN1YWxpemF0aW9uCgpIZXJlIGlzIHNvbWUgZ2VuZXJhbCBhZHZpY2UgdGhhdCBhcHBsaWVzIGluIHBvc3Qgc2l0dWF0aW9uczoKCjEuIFJlZHVjZSB0aGUgbnVtYmVyIG9mIHZpc3VhbCBlbGVtZW50cyB0aGF0IGFyZW4ndCBtYXBwZWQgdG8gdGhlIGRhdGEuIFRoaXMgY29ycmVzcG9uZHMgdG8gVHVmdGUncyBjb25jZXB0IG9mICJtaW5pbWFsIGluayIuIEF4ZXMgYW5kIGxhYmVscyBzaG91bGQgYmUgc2ltcGxlLiBJZiBhIHZpc3VhbCBlbGVtZW50IGRvZXNuJ3QgY2hhbmdlIHdoZW4gdGhlIGRhdGEgY2hhbmdlcywgYXNrIHlvdXJzZWxmIGlmIHlvdSByZWFsbHkgbmVlZCBpdC4KMi4gRG9uJ3QgbWFwIHRoZSBzYW1lIGRhdGEgYXR0cmlidXRlIHRvIG11bHRpcGxlIGFlc3RoZXRpYyBhdHRyaWJ1dGVzIChlZywgc2l6ZSBhbmQgY29sb3Igb2YgYSBwb2ludCkuIFRoaXMgaW5jcmVhc2VzIHRoZSBjb2duaXRpdmUgbG9hZCBvZiBpbnRlcnByZXR0aW5nIHRoZSB2aXN1YWxpemF0aW9uIHdpdGhvdXQgcHJvdmlkaW5nIGFkZGVkIHZhbHVlLgozLiBJZiB5b3UgYXJlIHJlZHVjaW5nIHRoZSBkYXRhLCB5b3UgYXJlIGVzc2VudGlhbGx5IGxvb2tpbmcgYXQgYSBzaGFkZG93IG9mIHRoZSBkYXRhLiBEaWZmZXJlbnQgb2JqZWN0cyBjYW4gY2FzdCB0aGUgc2FtZSBzaGFkZG93LCBzbyB0aGVyZSBpcyBhIHN0b25nIHBvc3NpYmlsaXR5IHRoYXQgeW91IGNvdWxkIG1pc3MgaW1wb3J0YW50IGZlYXR1cmVzIG9mIHRoZSBkYXRhLiBSZWR1Y2UgdGhlIGRhdGEgaW4gc2V2ZXJhbCBkaWZmZXJlbnQgd2F5cyB0byBjYXN0IGEgc2hhZGRvdyBmcm9tIGRpZmZlcmVudCBhbmdsZXMgdG8gdGVzdCB5b3VyIGFzc3VtcHRpb25zIGFib3V0IHdoYXQgeW91IGNhbid0IHNlZSBpbiBlYWNoIHBhcnRpY3VsYXIgdmlzdWFsaXphdGlvbi4KClBlb3BsZSBjYW4gZ2V0IHZlcnkgZG9nbWF0aWMgYW5kIGV4dHJlbWUgYWJvdXQgZGF0YSB2aXN1YWxpemF0aW9uIGdvYWxzLCBwYXJ0aWN1bGFybHkgaW4gdGhlIHBhcGxpY2F0aW9uIG9mIHBvaW50cyAxIGFuZCAyIGFib3ZlLiBBcyB3aXRoIGFueSBhcnRmb3JtLCBpbnRlcmVzdGluZyBhbmQgcHJvZHVjdGl2ZSB0aGluZ3MgY2FuIGhhcHBlbiB3aGVuIHlvdSBicmVhayB0aGUgInJ1bGVzIi4gSnVzdCBiZWNhdXNlIGl0IGlzIG9mdGVuIGEgYmFkIGlkZWEgdG8gYnJlYWsgdGhlIHJ1bGVzIGRvZXNuJ3QgbWVhbiBpdCBpcyBhbHdheXMgYmFkIHRvIGJyZWFrIHRoZSBydWxlcy4gSGVyZSBhcmUgYSBjb3VwbGUgcmVhc29ucyB0byBicmVhayB0aGUgcnVsZXM6CgotIEJlY2F1c2UgaXQgaXMgbW9yZSBiZWF1dGlmdWwgYW5kIGVuZ2FnaW5nIHRvIGRvIHNvLiBTb21ldGltZXMgYWRkaW5nIHZpc3VhbCBlbGVtZW50cyB0byBhIHBsb3QgdGhhdCBkb24ndCBtYXAgdG8gZGF0YSB3aWxsIGxlYWQgdG8gYSBtb3JlIGNvbXBlbGxpbmcgYW5kIGVuZ2FnaW5nIHZpc3VhbGl6YXRpb24uIFRvIHBhcmFwaHJhc2UgSmVyIFRob3JwZSAtICJJIGhhdmUgbWlsbGlvbnMgb2YgcGl4ZWxzIG9uIG15IHNjcmVlbiwgd2h5IGRvZXMgZXZlcnkgc2luZ2xlIG9uZSBuZWVkIHRvIG1hcCB0byBkYXRhPyIKLSBCZWNhdXNlIGl0IGVuaGFuY2VzIGEgdmlzdWFsIG1ldGFwaG9yZSB0aGF0IGxlYWRzIHRvIHlvdXIgdmlzdWFsaXphdGlvbiBiZWluZyBtb3JlIGludHVpdGl2ZSBhbmQgZmFtaWxpYXIuIEZvciBleGFtcGxlLCBpZiB5b3VyIGRhdGEgYXJlIGxhdGl0dWRlcyBhbmQgbG9uZ2l0dWRlcywgYnkgYWxsIG1lYW5zIHN1cGVyaW1wb3NlIHRoZW0gb24gYW4gaW1hZ2Ugb2YgRWFydGgsIGV2ZW4gdGhvdWdoIHRoYXQgaW1hZ2UgZG9lc24ndCBtYXAgdG8gZGF0YS4KCgojIyBFeGFtcGxlcwoKU2ltdWxhdGUgYSBkYXRhc2V0IG9mIHRocmVlIHZhcmFpYmxlcyBhY3Jvc3MgMTAwIG9ic2VydmF0aW9ucy4KCmBgYHtyfQpzZXQuc2VlZCgxMjM0NSkKICAjIFNpZ21hID0gbWF0cml4KGMobWF0cml4KGMoMTAsNiwxLDYsNSwyLDEsMiwxKSwzLDMpKSwzLDMpCiAgU2lnbWEgPSBtYXRyaXgoYyhtYXRyaXgoYygyLC0xLDAsLTEsMiwtMSwwLC0xLDIpLDMsMykpLDMsMykKICBEPWRhdGEuZnJhbWUobXZybm9ybShuPTEwMCwgbXU9cmVwKDAsMyksIFNpZ21hKSkKICBuYW1lcyhEKT1jKCJ4IiwgInkiLCAieiIpCmBgYAoKClN1YnNldCB0aGUgZGF0YSBieSBwbG90dGluZyBvbmx5IDIgb2YgdGhlIDMgdmFyaWFibGVzLgoKYGBge3J9CiAgbGlicmFyeShnZ3Bsb3QyKQoKICBnZ3Bsb3QoRCwKICAgIGFlcyh5PXksIHg9eCkpICsKICAgIGdlb21fcG9pbnQoKQpgYGAKCk1hcCBhbGwgdGhyZWUgYWVzdGhldGljIGF0dHJpYnV0ZXMuCgpgYGB7cn0KICBsaWJyYXJ5KGdncGxvdDIpCgogIGdncGxvdChELAogICAgYWVzKHk9eSwgeD14LCBjb2w9eikpICsKICAgIGdlb21fcG9pbnQoKQpgYGAKCgpgYGB7cn0KICBsaWJyYXJ5KGdncGxvdDIpCgogIGdncGxvdChELAogICAgYWVzKHk9eSwgeD14LCBzaXplPXopKSArCiAgICBnZW9tX3BvaW50KCkKYGBgCgoKCgpNYXAgeSB0byBtdWx0aXBsZSBhZXN0aGV0aWMgYXR0cmlidXRlcy4KCmBgYHtyfQogIGxpYnJhcnkoZ2dwbG90MikKCiAgZ2dwbG90KEQsCiAgICBhZXMoeT15LCB4PXgsIGNvbD15KSkgKwogICAgZ2VvbV9wb2ludCgpCmBgYAoKYGBge3J9CiAgbGlicmFyeShnZ3Bsb3QyKQoKICBnZ3Bsb3QoRCwKICAgIGFlcyh5PXksIHg9eCwgc2l6ZT15KSkgKwogICAgZ2VvbV9wb2ludCgpCmBgYAoKYGBge3J9CiAgbGlicmFyeShnZ3Bsb3QyKQoKICBnZ3Bsb3QoRCwKICAgIGFlcyh5PXksIHg9eCwgc2l6ZT15LCBjb2w9eSkpICsKICAgIGdlb21fcG9pbnQoKQpgYGAKCkFkZCBhZXN0aGV0aWMgYXR0cmlidXRlcyB0aGF0IGFyZW4ndCBtYXBwZWQgdG8gZGF0YS4KCmBgYHtyfQoKbGlicmFyeShwbmcpCmxpYnJhcnkoZ3JpZCkKCmltZyA8LSByZWFkUE5HKCJmaWd1cmVzL2NhdC5wbmciKQpnIDwtIHJhc3Rlckdyb2IoaW1nLCBpbnRlcnBvbGF0ZT1UUlVFKSAKCgogIGdncGxvdChELAogICAgYWVzKHk9eSwgeD14KSkgKwogICAgZ2VvbV9wb2ludCgpICsKICAgICNmYWNldF9ncmlkKC5+UGxhY2UpICsgCiAgICB0aGVtZV9idygpICsKICAgIGFubm90YXRpb25fY3VzdG9tKGcsIHhtaW49LUluZiwgeG1heD1JbmYsIHltaW49LUluZiwgeW1heD1JbmYpCgpgYGA=